﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace Ndo.Maths
{
    /// <summary>
    /// 级数
    /// 
    /// 泰勒展开式(幂级数展开法):
    /// f(x)=f(a)+f'(a)/1!*(x-a)+f''(a)/2!*(x-a)^2+...f(n)(a)/n!*(x-a)^n+...
    /// ex = 1+x+x2/2!+x3/3!+...+xn/n!+...
    /// ln(1+x)= x-x2/3+x3/3-...(-1)k-1*xk/k+...              (|x|<1)
    /// sin x = x-x3/3!+x5/5!-...(-1)k-1*x2k-1/(2k-1)!+...      (-∞<x<∞)
    /// cos x = 1-x2/2!+x4/4!-...(-1)k*x2k/(2k)!+...           (-∞<x<∞)
    /// arcsin x = x + 1/2*x3/3 + 1*3/(2*4)*x5/5 + ...       (|x|<1)
    /// arccos x = π - ( x + 1/2*x3/3 + 1*3/(2*4)*x5/5 + ... )      (|x|<1)
    /// arctan x = x - x^3/3 + x^5/5 - ...                   (x≤1)
    /// sinh x = x+x3/3!+x5/5!+...(-1)k-1*x2k-1/(2k-1)!+...      (-∞<x<∞)
    /// cosh x = 1+x2/2!+x4/4!+...(-1)k*x2k/(2k)!+...            (-∞<x<∞)
    /// arcsinh x = x - 1/2*x3/3 + 1*3/(2*4)*x5/5 - ...       (|x|<1)
    /// arctanh x = x + x^3/3 + x^5/5 + ...        (|x|<1)
    /// </summary>
    /// <typeparam name="TReal"></typeparam>
    public class Progression<TReal>
        where TReal: Real<TReal>, new()
    {
        public Progression(IEnumerable<TReal> ProgressionEnumeration)
        {
            this.ProgressionEnumeration = ProgressionEnumeration;
        }

        public delegate TReal FXnFunction(TReal x, TReal n);
        public Progression(TReal x, FXnFunction func, TReal startN)
            :this(x, func, startN, null, null)
        {}
        public Progression(TReal x, FXnFunction func, TReal startN, TReal endN)
            : this(x, func, startN, endN, null)
        {}

        public Progression(TReal x, FXnFunction func, TReal startN, TReal endN, TReal a0)
        {
            this.ProgressionEnumeration = new FXnProgression(x, func, startN, endN, a0);
        }
        private class FXnProgression : IEnumerable<TReal>
        {
            public FXnProgression(TReal x, FXnFunction func, TReal startN, TReal endN, TReal a0)
            {
                this.x = x;
                this.func = func;
                this.startN = startN;
                this.endN = endN;
                this.a0 = a0;
            }

            private TReal x;
            private FXnFunction func;
            private TReal startN;
            private TReal endN;
            private TReal a0;

            public IEnumerator<TReal> GetEnumerator()
            {
                if(a0 != null)
                {
                    yield return a0;
                }
                for(TReal n = startN; endN != null && n.Equals(endN); n.GetIncrement())
                {
                    yield return func(x, n);
                }
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                foreach (TReal number in this)
                {
                    yield return number;
                }
            }
        }

        public readonly IEnumerable<TReal> ProgressionEnumeration;

        public TReal GetValue()
        {
            TReal n = Number<TReal>.Zero;
            foreach (TReal number in ProgressionEnumeration)
            {
                n = n.Add(number);
            }
            return n;
        }

        public TReal GetValueWithNLimit<TInteger>(TInteger nLimit)
            where TInteger : Integer<TInteger>, new()
        {
            TInteger i = Integer<TInteger>.Zero;
            TReal n = Number<TReal>.Zero;
            foreach (TReal number in ProgressionEnumeration)
            {
                if(i.GreaterThanOrEqual(nLimit))
                    break;
                i = i.GetIncrement();
                n = n.Add(number);
            }
            return n;
        }

        public TReal GetValueWithTermPredicate(Predicate<TReal> termPredicate)
        {
            TReal n = Number<TReal>.Zero;
            foreach (TReal number in ProgressionEnumeration)
            {
                if (!termPredicate(number))
                    break;
                n = n.Add(number);
            }
            return n;
        }

        public TReal GetValueWithValuePredicate(Predicate<TReal> valuePredicate)
        {
            TReal n = Number<TReal>.Zero, nn;
            foreach (TReal number in ProgressionEnumeration)
            {
                nn = n.Add(number);
                if (!valuePredicate(nn))
                    break;
                n = nn;
            }
            return n;
        }

        public TReal GetValueWithPrecision()
        {
            TReal n = Number<TReal>.Zero;
            foreach (TReal number in ProgressionEnumeration)
            {
                TReal nn = n.Add(number);
                if (n.Equals(nn))
                    break;
            }
            return n;
        }

        public static IEnumerable<TReal> GetArithmetic(TReal a0, TReal d)
        {
            while (true)
            {
                yield return a0;
                a0 = a0.Add(d);
            }
        }
        public static IEnumerable<TReal> GetArithmetic(TReal a0, TReal d, TReal count)
        {
            while (count.Sign == SignTypes.Positive)
            {
                yield return a0;
                a0 = a0.Add(d);
                count = count.GetDecrement();
            }
        }

        public static IEnumerable<TReal> GetArithmetic<TInteger>(TReal a0, TReal d, TInteger count)
            where TInteger : Integer<TInteger>, new()
        {
            while (count.Sign == SignTypes.Positive)
            {
                yield return a0;
                a0 = a0.Add(d);
                count = count.GetDecrement();
            }
        }
        public static IEnumerable<TReal> GetGeometric(TReal a0, TReal q)
        {
            while (true)
            {
                yield return a0;
                a0 = a0.MultipliedBy(q);
            }
        }
        public static IEnumerable<TReal> GetGeometric(TReal a0, TReal q, TReal count)
        {
            while (count.Sign == SignTypes.Positive)
            {
                yield return a0;
                a0 = a0.MultipliedBy(q);
                count = count.GetDecrement();
            }
        }

        public static IEnumerable<TReal> GetGeometric<TInteger>(TReal a0, TReal q, TInteger count)
            where TInteger : Integer<TInteger>, new()
        {
            while (count.Sign == SignTypes.Positive)
            {
                yield return a0;
                a0 = a0.MultipliedBy(q);
                count = count.GetDecrement();
            }
        }

        /// <summary>
        /// e = 1/0!+1/1!+1/2!+1/3!+...1/n!...
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<TReal> GetE()
        {
            TReal i, f;
            i = Number<TReal>.Zero;
            f = Number<TReal>.One;
            while (true)
            {
                yield return f;
                i = i.GetIncrement();
                f = f.DividedBy(i);
            }
        }

        /// <summary>
        /// e^x = 1+x+x^2/2!+x^3/3!+...+x^n/n!+...
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<TReal> GetEx(TReal x)
        {
            TReal i, f;
            i = Number<TReal>.Zero;
            f = Number<TReal>.One;
            while (true)
            {
                yield return f;
                i = i.GetIncrement();
                f = f.MultipliedBy(x);
                f = f.DividedBy(i);
            }
        }

        /// <summary>
        /// sin x = x-x^3/3!+x^5/5!-...(-1)^(k-1)*x^(2k-1)/(2k-1)!+...      (-∞<x<∞)
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<TReal> GetSin(TReal x)
        {
            TReal i, f, r;
            i = Number<TReal>.One;
            f = Number<TReal>.NegativeOne;
            r = x.GetSqr();
            while (true)
            {
                yield return x;
                i = i.GetIncrement();
                f = f.MultipliedBy(i);
                i = i.GetIncrement();
                f = f.MultipliedBy(i);
                x = x.MultipliedBy(r);
                x = x.DividedBy(f);
            }
        }

        /// <summary>
        /// cos x = 1-x^2/2!+x^4/4!-...(-1)^k*x^(2k)/(2k)!+...           (-∞<x<∞)
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<TReal> GetCos(TReal x)
        {
            TReal i, f, r;
            i   = Number<TReal>.Zero;
            f  = Number<TReal>.NegativeOne;
            r  = x.GetSqr();
            x   = Number<TReal>.One;
            while (true)
            {
                yield return x;
                i = i.GetIncrement();
                f = f.MultipliedBy(i);
                i = i.GetIncrement();
                f = f.MultipliedBy(i);
                x = x.MultipliedBy(r);
                x = x.DividedBy(f);
            }
        }
    }
}
